package com.rd.config;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;

import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.net.UnknownHostException;
import java.time.Duration;

//@EnableCaching
@Configuration
public class RedisConfig {

    // 设置预期插入数据
    private static final Long EXPECT_INSERT = 1000000L;
    //设置误判率
    private static final double ERROR_RATIO = 0.02;

    // 将RedissonClient对象注入IOC容器
    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }

    //注入布隆过滤器
    @Bean
    public RBloomFilter bloomFilter() {
        //获取布隆过滤器
        RBloomFilter<Object> bloomFilter = redissonClient().getBloomFilter("bloom-filter");
        //设置过滤器参数
        bloomFilter.tryInit(EXPECT_INSERT,ERROR_RATIO);

        return bloomFilter;
    }

    // 自定义 RedisTemplate，默认使用jdk序列化
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Object> jackson2RedisSerializer = jackson2JsonRedisSerializer();

        // 因此，需要改成 json格式的序列化
        template.setDefaultSerializer(jackson2RedisSerializer);
        template.setKeySerializer(jackson2RedisSerializer);
        template.setValueSerializer(jackson2RedisSerializer);
        template.setHashKeySerializer(jackson2RedisSerializer);
        template.setHashValueSerializer(jackson2RedisSerializer);
        template.afterPropertiesSet();

        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper()
                .registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule());
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        template.setValueSerializer(serializer);

        return template;
    }

    //    //重写 RedisCacheConfiguration 类的cacheManager方法 并注入IOC容器
    @Primary   // 设置为默认缓存管理器
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory){

        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = jackson2JsonRedisSerializer();

        RedisCacheConfiguration cacheManager =
                RedisCacheConfiguration.defaultCacheConfig()
                        //设置缓存有效时间(1小时)
                        .entryTtl(Duration.ofHours(1))
                        //不缓存null结果，若出现null结果时会报异常
                        .disableCachingNullValues()
                        //以json形式序列化对象
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer));
        return RedisCacheManager.builder(factory).cacheDefaults(cacheManager).build();
    }

    // 配置Jackson2JsonRedisSerializer
    // 避免出现获取缓存时出现的类型转换错误
    private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        // 此项必须配置，否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }

}
